import os
import re
from enum import Enum
from docx import Document
from docx.shared import Pt, Cm, RGBColor
from docx.enum.section import WD_ORIENT
from docx.enum.text import WD_PARAGRAPH_ALIGNMENT
from docx.enum.table import WD_ALIGN_VERTICAL

from src import date


class Duty:
    def __init__(self, value="", lock=False):
        self.value = value
        self.lock = lock
    
    @staticmethod
    def is_rest(value):
        return value in {'休', '业余', '业余生长', '庆春业余', '生长肥胖', '生长脱敏', '庆春休', '余杭休', '特需', '特需休'}

    def __str__(self) -> str:
        return self.value

class Schedule:
    def __init__(self, year=None, month=None):
        if year is None or month is None:
            year, month = date.get_next_month()
        self.min_year = 2020
        self.max_year = 2050
        self.min_month = 1
        self.max_month = 12
        self.year = year
        self.month = month
        self.days = date.get_days(year, month)
        self.weekdays = date.get_weekdays(year, month)
        self.holidays = set()
        self.dutys = {}
        self.row_stats = {}
        self.col_stats = {}
        self.init_holidays()
        self.init_dutys()
        self.tables = {}
        self.filename = ""
        self.changed = False

    def init_holidays(self):
        self.holidays.clear()
        for day in self.days:
            weekday = date.get_weekday(self.year, self.month, day)
            if weekday == "六" or weekday == "日":
                self.holidays.add(day)

    def init_dutys(self):
        self.dutys.clear()
        self.dutys['正班'] = ['正', '正备', '正73', '正1', '正2', '正3', '正4', '正5', '门接']
        self.dutys['门诊'] = ['门', '门庆春', '门余杭', '正门', '门正', '门休', '生长发育']
        self.dutys['值班'] = ['值1', '值2', '值休']
        self.dutys['休息'] = ['休']
        self.dutys['业余'] = ['业余', '业余生长', '庆春业余', '生长肥胖', '生长脱敏', '庆春休', '余杭休', '特需', '特需休']
        self.dutys['其他'] = ['援外', '培训', '年休']

    def load_table(self, table):
        rows = len(table.rows)
        cols = len(table.columns)
        if rows < 3 or cols < 3:
            return
        days = len(self.days)
        cells = table._cells
        # 提取节假日
        has_days = False
        for i in range(0, rows):
            row = cells[i * cols : (i + 1) * cols]
            for j in range(2, cols):
                cell = row[j]
                text = cell.text.strip()
                if text.isdigit():
                    has_days = True
                    day = int(text)
                    rgb = cell.paragraphs[0].runs[0].font.color.rgb
                    if day <= days and rgb and rgb[0] > rgb[1] and rgb[0] > rgb[2]:
                        self.holidays.add(day)
            if has_days:
                break
        if not has_days:
            self.init_holidays()
        # 用第一列的第一个值作为表格的id
        id = ""
        for i in range(0, rows):
            if id:
                break
            id = cells[i * cols].text.strip()
        # 重名的表格直接复用
        if id in self.tables:
            data = self.tables[id]
        else:
            data = {}
            self.tables[id] = data
        # 提取表格中的数据
        for i in range(0, rows):
            row = cells[i * cols : (i + 1) * cols]
            # 提取排班数据
            name = row[1].text.strip()
            if not name:
                continue
            # 重复出现的名字用后面的行刷新非空值
            if name in data:
                vals = data[name]
            else:
                vals = [Duty() for day in self.days]
                data[name] = vals
            for j in range(2, cols):
                cell = row[j]
                val = cell.text.strip()
                if val:
                    duty = vals[j - 2]
                    duty.value = val
                    if len(cell.paragraphs[0].runs) > 0:
                        duty.lock = True if cell.paragraphs[0].runs[0].font.bold else False

    def add_par(self, doc, align="center"):
        par = doc.add_paragraph()
        par.alignment = WD_PARAGRAPH_ALIGNMENT.from_xml(align)
        par.paragraph_format.space_after = 0
        return par

    def add_run(self, par, text, size=11, color="000000", bold=False):
        run = par.add_run(text)
        run.font.size = Pt(size)
        run.font.color.rgb = RGBColor.from_string(color)
        run.font.bold = bold

    def set_cell(self, cell, text, align="center", bold=False):
        par = cell.paragraphs[0]
        par.alignment = WD_PARAGRAPH_ALIGNMENT.from_xml(align)
        run = par.add_run(text)
        run.font.bold = bold

    def add_table(self, doc, id, style="Table Grid"):
        data = self.tables[id]
        rows = len(data) + 2
        cols = len(self.days) + 2
        # 插入表格
        table = doc.add_table(rows, cols, style)
        cells = table._cells
        for cell in cells:
            cell.vertical_alignment = WD_ALIGN_VERTICAL.from_xml("center")
        # 设置第一列为标题
        self.set_cell(cells[0], id)
        cells[0].merge(cells[(rows - 1) * cols])
        # 第一行添加日期
        row = cells[2:cols]
        for i, day in enumerate(self.days):
            self.set_cell(row[i], str(day))
        # 第二行添加星期
        row = cells[cols + 2 : 2 * cols]
        for i, day in enumerate(self.weekdays):
            self.set_cell(row[i], str(day))
        # 添加每个人排班
        for i, name in enumerate(data.keys()):
            row = cells[(i + 2) * cols + 1: (i + 3) * cols]
            self.set_cell(row[0], name)
            for j, duty in enumerate(data[name]):
                self.set_cell(row[j + 1], duty.value, bold=duty.lock)
        # 设置节假日为红色
        for i in range(0, rows):
            for j in self.holidays:
                run = cells[i * cols + j + 1].paragraphs[0].runs[0]
                run.font.color.rgb = RGBColor.from_string('ff0000')

    def load(self, filename=None):
        if filename is None:
            filename = self.filename
        if not os.path.exists(filename):
            return f"文件不存在"
        doc = Document(filename)
        firstline = doc.paragraphs[0].text
        nums = re.findall(r"\d+", firstline)
        if len(nums) < 2:
            return f"首行无年月数字（如2024.4）"
        year = int(nums[0])
        month = int(nums[1])
        if year < self.min_year or year > self.max_year:
            return f"无效的年份数字：{year}"
        if month < self.min_month or month > self.max_month:
            return f"无效的月份数字：{month}"
        self.year = year
        self.month = month
        self.days = date.get_days(self.year, self.month)
        self.weekdays = date.get_weekdays(self.year, self.month)
        self.holidays.clear()
        self.tables.clear()
        for table in doc.tables:
            self.load_table(table)
        self.stat()
        self.filename = filename
        self.changed = False

    def save(self, filename=None):
        if filename is None:
            filename = self.filename
        print(filename)
        doc = Document()
        # 设置默认字体
        font = doc.styles["Normal"].font
        font.name = "宋体"
        font.size = Pt(11)
        # 设置页面
        sec = doc.sections[0]
        sec.orientation = WD_ORIENT.LANDSCAPE
        sec.page_width = Cm(29.7)
        sec.page_height = Cm(21)
        sec.top_margin = Cm(0.5)
        sec.bottom_margin = Cm(0.5)
        sec.left_margin = Cm(1)
        sec.right_margin = Cm(1)
        # 设置标题
        par = self.add_par(doc)
        self.add_run(par, f"{self.year}.{self.month} 休", 14)
        self.add_run(par, f"{len(self.holidays)}", 14, "ff0000")
        self.add_run(par, f"天", 14)
        # 保存表格
        for id in self.tables:
            self.add_table(doc, id)
            self.add_par(doc)
        doc.save(filename)
        if not os.path.exists(filename):
            return f"保存失败"
        self.filename = filename
        self.changed = False

    def clean(self, id):
        for name in self.tables[id]:
            data = self.tables[id][name]
            for duty in data:
                if duty.lock:
                    continue
                duty.value = ""
        self.changed = True

    def toggle_holiday(self, day):
        if day in self.holidays:
            self.holidays.remove(day)
        else:
            self.holidays.add(day)
        self.changed = True

    def stat(self):
        self.row_stats.clear()
        for id in self.tables:
            for name in self.tables[id]:
                self.stat_row(id, name)
        self.col_stats.clear()
        for id in self.tables:
            for day in self.days:
                self.stat_col(id, day)

    def stat_row(self, id, name):
        if id not in self.tables:
            return
        table = self.tables[id]
        if name not in table:
            return
        if id not in self.row_stats:
            self.row_stats[id] = {}
        stats = self.row_stats[id]
        stats[name] = {}
        stat = stats[name]
        row = table[name]
        for duty in row:
            if not duty.value:
                continue
            if duty.value in stat:
                stat[duty.value] += 1
            else:
                stat[duty.value] = 1
    
    def stat_col(self, id, day):
        if id not in self.tables:
            return
        table = self.tables[id]
        if day not in self.days:
            return
        if id not in self.col_stats:
            self.col_stats[id] = {}
        stats = self.col_stats[id]
        stats[day] = {}
        stat = stats[day]
        for name in table:
            duty = table[name][day - 1]
            if not duty.value:
                continue
            if duty.value in stat:
                stat[duty.value] += 1
            else:
                stat[duty.value] = 1